using System;
using ErrorDumper.Filtering;
using ErrorDumper.Processing;

namespace ErrorDumper.Handlers {
    class GeneralExceptionHandler : IExceptionHandler {
        public GeneralExceptionHandler(IExceptionHandler innerHandler) {
            if ((_innerHandler = innerHandler) == null)
                throw new ArgumentNullException("innerHandler");
        }

        readonly IExceptionHandler _innerHandler;

        public string HandleException(ExceptionPropertyCollection properties, Exception exception) {
            try {
                if (exception != null) {
                    properties = ExceptionPropertyCollection.Build(properties, exception, false);

                    if (properties == null)
                        properties = new ExceptionPropertyCollection();

                    if (!properties.Contains(ExceptionProperty.ClassName))
                        properties[ExceptionProperty.ClassName] = exception.GetType().Name;

                    if (!properties.Contains(ExceptionProperty.ClassFullName))
                        properties[ExceptionProperty.ClassFullName] = exception.GetType().FullName;

                    if (!properties.Contains(ExceptionProperty.Message))
                        properties[ExceptionProperty.Message] = exception.Message;
                }

                var level = ExceptionFilters.HandleException(properties, exception);

                var context = new ExceptionProcessorContext(exception) {
                    Properties = properties,
                    HandlingLevel = level
                };
                ExceptionProcessors.ProcessException(context);
                properties = context.Properties;
                var content = context.Content;
                level = context.HandlingLevel;

                switch (level) {
                    case ExceptionHandlingLevel.Ignore:
                        return null;
                    case ExceptionHandlingLevel.WriteProperties:
                        return _innerHandler.HandleException(properties, (string)null);
                    case ExceptionHandlingLevel.WriteContent:
                        if (content == null)
                            content = ExceptionSerializer.SerializeException(
                                properties, exception, ExceptionSerializationOptions.MinimumSerialization);
                        return _innerHandler.HandleException(properties, content);
                    case ExceptionHandlingLevel.WriteExtendedDump:
                        if (content == null)
                            content = ExceptionSerializer.SerializeException(
                                properties, exception, ExceptionSerializationOptions.MaximumSerialization);
                        return _innerHandler.HandleException(properties, content);
                    default:
                        throw new NotSupportedException(level.ToString());
                }
            }
            catch (Exception ex) {
                _innerHandler.HandleExceptionSafely(ex);
                return null;
            }
        }

        public string HandleException(ExceptionPropertyCollection properties, string content) {
            try {
                var level = ExceptionFilters.HandleException(properties, content);

                var context = new ExceptionProcessorContext {
                    Properties = properties,
                    Content = content,
                    HandlingLevel = level
                };
                ExceptionProcessors.ProcessException(context);
                properties = context.Properties;
                content = context.Content;
                level = context.HandlingLevel;
                
                switch (level) {
                    case ExceptionHandlingLevel.Ignore:
                        return null;
                    case ExceptionHandlingLevel.WriteProperties:
                        return _innerHandler.HandleException(properties, (string)null);
                    case ExceptionHandlingLevel.WriteContent:
                        goto case ExceptionHandlingLevel.WriteExtendedDump;
                    case ExceptionHandlingLevel.WriteExtendedDump:
                        return _innerHandler.HandleException(properties, content);
                    default:
                        throw new NotSupportedException(level.ToString());
                }
            }
            catch (Exception ex) {
                _innerHandler.HandleExceptionSafely(ex);
                return null;
            }
        }
    }
}